feat(shell): self-report shell identity via OSC 9001;ShellType#345
Conversation
Shells now emit OSC 9001;ShellType;<name>;<version> on every prompt so the terminal always knows which shell owns a pane, even after a nested shell (pwsh -> wsl -> exit) returns. Replicates the WorkingDirectory data flow: adaptDispatch -> ITerminalApi::SetShellType -> Terminal -> ControlCore -> ICoreState -> TermControl -> PaneInfo -> COM JSON (shell/shell_version), so wtcli list-panes / get-active-pane expose the live shell per pane. PowerShell reports pwsh/powershell; bash reports bash or wsl:<distro>. Fixes autofix recommending PowerShell commands in a WSL/bash pane: the autofix Shell Context now prefers the OSC-reported shell over the pid-based process-image lookup, which can't see the real foreground shell inside a nested wsl/pwsh host process. Also removes the dead OSC 9001 WtaReq/WtaRes in-band handler (superseded by the out-of-band COM IProtocolServer channel). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR adds a new shell-integration signal (OSC 9001;ShellType;<name>;<version>) that lets the foreground shell report its identity on every prompt, and propagates that information through the Terminal core, control/app layers, and the Terminal Protocol COM JSON so WTA/autofix can reliably choose shell-appropriate fixes (especially across nested shells like pwsh → wsl → exit).
Changes:
- Adds
ShellTypehandling in the VT action dispatcher and a newITerminalApi::SetShellTypeplumbing path to store shell name/version inTerminaland expose it throughControlCore/TermControlinto protocolPaneInfoand COM JSON (shell,shell_version). - Updates PowerShell and bash/WSL shell integration snippets to emit
OSC 9001;ShellTypeeach prompt. - Updates WTA’s pane JSON parsing to prefer the OSC-reported
shellfield (with tests) and fall back to pid-based resolution when absent/empty.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/wta/src/protocol/acp/client.rs | Prefer OSC-reported shell in active-pane JSON; adds unit test for precedence/fallback behavior. |
| src/terminal/adapter/ut_adapter/adapterTest.cpp | Updates test mock ITerminalApi implementation with SetShellType. |
| src/terminal/adapter/ITerminalApi.hpp | Adds SetShellType(shellName, shellVersion) to the terminal API contract. |
| src/terminal/adapter/adaptDispatch.cpp | Implements OSC 9001;ShellType WT action parsing and forwards to ITerminalApi. |
| src/host/outputStream.hpp | Adds SetShellType to conhost internal API surface (no-op implementation). |
| src/host/outputStream.cpp | No-op SetShellType for conhost, documenting feature is WT-only. |
| src/cascadia/WindowsTerminal/TerminalProtocolComServer.cpp | Serializes pane shell and shell_version into protocol JSON. |
| src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp | Adds TerminalCore unit test validating ShellType parsing/state behavior. |
| src/cascadia/TerminalProtocol/TerminalProtocol.idl | Extends PaneInfo with Shell and ShellVersion. |
| src/cascadia/TerminalCore/TerminalApi.cpp | Implements Terminal::SetShellType and one-time telemetry event. |
| src/cascadia/TerminalCore/Terminal.hpp | Stores shell name/version and adds getters + ITerminalApi override. |
| src/cascadia/TerminalCore/Terminal.cpp | Implements GetShellName() / GetShellVersion() accessors. |
| src/cascadia/TerminalControl/TermControl.h | Adds ShellName() / ShellVersion() accessors to TermControl. |
| src/cascadia/TerminalControl/TermControl.cpp | Wires TermControl shell accessors through to ControlCore. |
| src/cascadia/TerminalControl/ICoreState.idl | Adds ShellName / ShellVersion properties to core state interface. |
| src/cascadia/TerminalControl/ControlCore.h | Adds ShellName() / ShellVersion() accessors to ControlCore. |
| src/cascadia/TerminalControl/ControlCore.cpp | Reads shell name/version under terminal read lock for the control layer. |
| src/cascadia/TerminalApp/TerminalPage.Protocol.cpp | Plumbs shell name/version into PaneInfo returned by protocol APIs. |
| src/cascadia/inc/PowerShellShellIntegration.h | Emits OSC 9001;ShellType each PowerShell prompt with edition + version. |
| src/cascadia/inc/BashShellIntegration.h | Emits OSC 9001;ShellType each bash prompt; reports wsl:<distro> under WSL. |
- TerminalApi.cpp: make the one-shot ShellType telemetry gate race-free (function-static logged is shared across Terminal instances holding different locks; switch to std::atomic<bool>::exchange). - Remove dangling references to the deleted doc/specs/shell-integration-and-osc9001.md spec from code comments (adaptDispatch.cpp, BashShellIntegration.h, client.rs). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Add a WSL-gated Describe to Feature.AutofixPane that exercises OSC 9001;ShellType end-to-end: Case 1 (deterministic) asserts a WSL pane self-reports shell=wsl:<distro> through the protocol (scoped by tab id, since list-panes is active-tab scoped); Case 2 (AI oracle) verifies autofix suggests Linux/bash syntax, not PowerShell, in a WSL pane. The whole Describe skips unless a dev package + copilot + winapp + a runnable WSL distro are present. Framework fix: UI-dependent suites gated only on package/copilot would throw a raw 'winapp not found' in BeforeAll when the Windows App CLI isn't installed, instead of skipping. Add a non-throwing Test-WinAppAvailable helper (exported) and fold winapp into the readiness gates of every agent-pane / FRE-overlay suite. Packaging keeps its §9 protocol Describe runnable without winapp; only the §10 UI Describe is winapp-gated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
…s existing users The OSC 9001;ShellType emission was added to the v1 shell-integration scripts without bumping kVersion. The install orchestrator's block-match early-out (ShellIntegrationCommon.h:414) only rewrites the on-disk script when the \C:\Users\kaitao\OneDrive - Microsoft\Documents\PowerShell\Microsoft.PowerShell_profile.ps1/.bashrc sourcing block changes or the script file is missing. Since the block embeds the versioned filename and the filename was unchanged, existing users kept their stale v1 script (no ShellType) and the feature silently never reached them — verified live: a pwsh pane reported shell='' until the bump. Bump both PowerShell and Bash (WSL inherits via WslBashFlavor) to v2 so the block changes -> the orchestrator rewrites the script in place. Verified end-to-end: after deploy, shell-integration_v2.ps1 is written with the 9001 emission, the profile block is rewritten to reference it (with a backup), and a pwsh pane now reports shell='pwsh' 7.4.14. Add regression tests Install_/Bash_Install_UpgradesWhenBlockReferences- OlderScriptVersion: a managed block pointing at an older script version plus a stale on-disk script must upgrade (alreadyInstalled=false), and the rewritten script must contain the OSC 9001 emission. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@check-spelling-bot Report
|
| Dictionary | Entries | Covers | Uniquely |
|---|---|---|---|
| cspell:csharp/csharp.txt | 32 | 2 | 2 |
| cspell:aws/aws.txt | 232 | 2 | 2 |
| cspell:fonts/fonts.txt | 536 | 1 | 1 |
Consider adding to the extra_dictionaries array (in the .github/actions/spelling/config.json file):
"cspell:csharp/csharp.txt",
"cspell:aws/aws.txt",
"cspell:fonts/fonts.txt",
To stop checking additional dictionaries, put (in the .github/actions/spelling/config.json file):
"check_extra_dictionaries": []Warnings ⚠️ (1)
See the 📂 files view, the 📜action log, 👼 SARIF report, or 📝 job summary for details.
| Count | |
|---|---|
| 54 |
See
✏️ Contributor please read this
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
If the listed items are:
- ... misspelled, then please correct them instead of using the command.
- ... names, please add them to
.github/actions/spelling/allow/names.txt. - ... APIs, you can add them to a file in
.github/actions/spelling/allow/. - ... just things you're using, please add them to an appropriate file in
.github/actions/spelling/expect/. - ... tokens you only need in one place and shouldn't generally be used, you can add an item in an appropriate file in
.github/actions/spelling/patterns/.
See the README.md in each directory for more information.
🔬 You can test your commits without appending to a PR by creating a new branch with that extra change and pushing it to your fork. The check-spelling action will run in response to your push -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. 😉
If the flagged items are 🤯 false positives
If items relate to a ...
-
binary file (or some other file you wouldn't want to check at all).
Please add a file path to the
excludes.txtfile matching the containing file.File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.
^refers to the file's path from the root of the repository, so^README\.md$would exclude README.md (on whichever branch you're using). -
well-formed pattern.
If you can write a pattern that would match it,
try adding it to thepatterns.txtfile.Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.
Note that patterns can't match multiline strings.
The feature and self-test Describes hardcoded -Package Store in their BeforeAll/It launch calls, so on a dev-only machine (only the sideload package installed) they could not run as-written. Route every launch through a new Get-ItTestPackage selector that honors the ITE2E_PACKAGE env var and defaults to Auto (prefer a resolvable Store install, else Dev). Start-TerminalFre's default param now uses the same selector. This lets the whole suite validate against the dev build without edits; set ITE2E_PACKAGE=Store to pin to the store build in CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eout The Insert and Run autofix retry loops wrapped Wait-Autofix in try/finally WITHOUT a catch, so a single 45s Wait-Autofix timeout propagated and failed the test before the loop could try the next typo or reach the LLM-variance skip guard. The sibling card-render loop already swallows this with an empty catch. Add the same catch so an explain-only (no-card) run skips instead of hard-failing, matching the documented LLM-variance behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WSL in-distro agent session management (#323) is being rolled back for this release, so drop its user-facing claims from the release notes: the intro clause, feature #1's host+in-WSL/resume wording (now host-only, #305/#365), and the WSL example in the F5-refresh entry. OSC 9001 ShellType (#345) stays as it is shell-identity detection, not the WSL session feature. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(release-check-list): sync with v0.1.2 features Reviewed the release checklist against the v0.1.2 release notes and fixed two outdated statements plus added seven missing test cases for new features. Outdated statements corrected: - Session view refresh: now F5 on-demand re-scan (incl. WSL distros started after launch), not gated on hooks (#344). - Historical state: history is now sourced from ACP session/list, not on-disk file parsing (#365). Missing test cases added: - Alt+V clipboard image paste into agent chat (#354) - GitHub Enterprise Copilot sign-in (#362) - Bash/WSL shell integration incl. set -u safety (#340) - Shells self-report identity via OSC 9001;ShellType + nested-shell autofix targeting (#345) - Environment-aware answers/fixes: investigate PATH before answering/autofixing (#306) - WSL distro sessions visible and resumable in the correct distro (#323) - Agent panes not persisted into saved window layout (#360/#275) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(release-check-list): tag new v0.1.2 cases with [new] + add 5 more Second review pass over the release checklist against the v0.1.2 release notes: - Added a [new] coverage marker to the legend and tagged all 12 genuinely-new v0.1.2 test cases with it (the two reworded existing cases — F5 refresh, ACP-sourced historical — are corrections, not new cases, so they stay untagged). - Added 5 more previously-missing cases surfaced by a closer read: - FRE execution-policy detection is correct / no false-block on probe timeout (#336/#338/#309) - wta-master death is a consistent degraded state requiring /restart, no split-brain (#329) - Session titles are clean (no bare '# AGENTS.md instructions' heading) (#355) - Focus brings the target window to the foreground (#353) - Agent-created terminals inherit the active pane profile (#366, closes #351) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(e2e): add master-death degraded-state integration test (#329); drop rolled-back WSL session checklist item Adds Feature.AgentMasterDeath.Tests.ps1: kills this app's wta-master out from under a live helper and asserts the full #329 contract end-to-end — the pane leaves Connected, the / popup is filtered to only /restart, NO master is silently respawned while degraded (anti-split-brain), and /restart brings up exactly one fresh master and reconnects. Verified passing live against the deployed dev package. Also removes the '[new] WSL distro sessions are visible and resumable' checklist item (#323) since WSL in-distro session support is being rolled back for v0.1.2, and drops the WSL example from the F5 refresh item to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address Copilot review: tighten master-count assertion and clarify Alt+V behavior - Feature.AgentMasterDeath.Tests.ps1: assert exactly one wta-master while connected (-Be 1, was -BeGreaterThan 0) so the connected-state gate itself catches a split-brain regression. Re-verified passing live. - release-check-list.md: correct the Alt+V item — Alt+V queues the image until the next prompt, and an unsupported agent / empty clipboard surfaces a clear system message rather than being a silent no-op. The [WSL-<distro>] raw-angle-bracket comment is already resolved: that checklist item was removed with the WSL session rollback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address Copilot review round 2: block /stop in degraded popup test, release-agnostic [new] marker, spelling fix - Feature.AgentMasterDeath.Tests.ps1: add /stop to the blocked-slash-command list so a regression that offers /stop while transport-lost is caught (it's a real CommandKind::Stop). Re-verified passing live. - release-check-list.md: reword the [new] marker in release-agnostic terms and say when to clear it (was hard-coded to v0.1.2); fix forbidden spelling non-existent -> nonexistent (check-spelling CI). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
What
Lets shells self-report their identity each prompt via the private
OSC 9001;ShellType;<name>;<version>sub-action, so the terminal always knows which shell owns a pane — even after a nested shell (pwsh->wsl->exit) returns, where there's no reliable "I exited" signal.Why
Autofix was recommending PowerShell commands (e.g.
Get-ChildItem) inside a WSL/bash pane. The autofix shell context derivedshellfrom the pane's pid -> process image name, which inside awsl.exe/pwsh.exehost can't see the real foreground shell drawing the prompt. The prompt is the natural "who's in charge now" signal, so the shell reports it on every prompt.How
PowerShellShellIntegration.hreportspwsh/powershell+ version;BashShellIntegration.hreportsbashorwsl:<distro>+ version (WSL reusesBashFlavor, so one edit covers Git Bash + every distro).WorkingDirectorydata flow end to end —adaptDispatch DoWTAction->ITerminalApi::SetShellType->Terminal->ControlCore->ICoreState->TermControl->PaneInfo-> COM_toJson(shell/shell_version).wtcli list-panes/get-active-panenow expose the live shell per pane.shell_from_active(autofix) now prefers the OSC-reportedshellover the pid lookup; pid stays as the fallback for panes without shell integration.kVersion(1 -> 2) so existing users actually receive the newShellTypeline — the install orchestrator only rewrites the managed block when the versioned script filename changes.WtaReq/WtaResin-band handler (superseded by the out-of-band COMIProtocolServerchannel).Test
UnitTests_TerminalCoreTerminalApiTest::SetShellType— feeds the OSC, assertsGetShellName()/GetShellVersion()(name+version, name-only, empty, last-writer-wins, no interference withCmdNotFound).ShellIntegrationTests*_UpgradesWhenBlockReferencesOlderScriptVersion— the kVersion bump reaches existing users (block referencing an older script version is rewritten; new script contains9001).shell_from_active_prefers_osc_reported_shell+ existingshell_from_active_resolves_pid— OSC field wins over pid; empty/whitespace falls back.Feature.AutofixPane.Tests.ps1, gated on a runnable WSL distro + the dev package): WSL pane self-reportswsl:<distro>through the protocol, and autofix suggests bash (not PowerShell) syntax.bcz no_cleanbuild: 0 errors. Manually verified on the dev package: a WSL pane reportswsl:<distro>and autofix suggests bash-syntax fixes.Closes #352 (context awareness: agent pane & autofix recognize the active WSL/bash shell). Part of #200.